package com.xiu.json.engine;

import com.xiu.json.JSONArray;
import com.xiu.json.JSONObject;
import com.xiu.json.exception.ParseJsonException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: tjc
 * @description
 * @Date Created in 23:12 2022-04-02
 */
public class DefaultJSONEngine {
    TokenIterator iter;

    public DefaultJSONEngine(String str) {
        iter = new TokenParser(str).Iterator();
    }

    public Object parse() {
        Token token = nextToken();
        if ("{".equals(token.value)) {
            return new JSONObject(parseObj());
        } else if ("[".equals(token.value)) {
            return new JSONArray(parseArray());
        }
        throw new ParseJsonException("expecting delimiter:" + token.value);
    }

    public Map<String, Object> parseObj() {
        Map<String, Object> elements = new HashMap<>();
        while (true) {
            Token key = nextToken();
            Token colon = nextToken();
            if ("}".equals(key.value)) {
                return elements;
            }
            if (key.type == TokenType.STRING && colon.value.equals(":")) {
                Token val = nextToken();
                switch (val.type) {
                    case END -> throw new ParseJsonException("unexcept end.");
                    case NUMBER, STRING, NULL, BOOL -> {
                        elements.put(key.value, convertBasicValue(val));
                    }
                    case SYMBOL -> {
                        switch (val.value) {
                            case "[" -> elements.put(key.value, parseArray());
                            case "{" -> elements.put(key.value, parseObj());
                            default -> throw new ParseJsonException("unexcept token:" + val.value);
                        }
                    }
                    default -> throw new ParseJsonException("unexcept token:" + val.value);
                }
                Token end = nextToken();
                if (",".equals(end.value)) {
                    continue;
                } else if ("}".equals(end.value)) {
                    return elements;
                } else {
                    throw new ParseJsonException("unexcept token:" + end.value);
                }
            } else {
                throw new ParseJsonException("unexcept token" + key.value + colon.value);
            }
        }
    }

    private Object[] parseArray() {
        List<Object> elements = new ArrayList<>();
        while (true) {
            Token t = nextToken();
            switch (t.type) {
                case END -> throw new ParseJsonException("unexcept end.");
                case SYMBOL -> {
                    switch (t.value) {
                        case "]":
                            return elements.toArray();
                        case "{":
                            elements.add(parseObj());
                            continue;
                        case ",":
                            continue;
                        default:
                            throw new ParseJsonException("unexcept token:" + t.value);
                    }
                }
                case NUMBER, STRING, NULL, BOOL -> {
                    elements.add(convertBasicValue(t));
                    continue;
                }
                default -> throw new ParseJsonException("unexcept token:" + t.value);
            }
        }
    }

    private Object convertBasicValue(Token t) {
        String value = t.value;
        switch (t.type) {
            case NULL:
                return null;
            case STRING:
                return value;
            case BOOL:
                return value.equals("true") ? Boolean.TRUE : Boolean.FALSE;
            case NUMBER: {
                boolean point = value.contains(".");
                Number num;
                try {
                    if (point)
                        num = Float.valueOf(value);
                    else
                        num = Integer.valueOf(value);
                } catch (NumberFormatException e) {
                    num = Long.valueOf(value);
                }
                return num;
            }
            default:
                throw new ParseJsonException("Unsupported convert type");
        }
    }

    private Token parsePair() {
        Token key = nextToken();
        if (key.type == TokenType.STRING && nextToken().value.equals(":")) {
            Token val = nextToken();
            if (val.value.equals("{")) {

            } else if (val.value.equals("[")) {

            } else {
                switch (val.type) {
                    case END, NUMBER:
                }
            }

        } else {
            throw new ParseJsonException("unexcept token:" + key.value);
        }
        return null;
    }

    private Token nextToken() {
        return iter.next();
    }
}
